import type { ImageMetadata, RestoredImage } from '../types/models';
import { validateMetadataDimensions } from '../utils/metadataUtils';
import { ValidationError, ProcessingError } from '../utils/errorUtils';

/**
 * Service for handling image restoration operations
 */
export class ImageRestorationService {
  /**
   * Validate if metadata matches the image
   * @param image Stitched image file
   * @param metadata Image metadata
   * @returns Promise with validation result and error message
   */
  async validateMetadata(image: File, metadata: ImageMetadata): Promise<{ valid: boolean; message?: string }> {
    try {
      // Check if metadata has the required fields
      if (!metadata.version || !metadata.timestamp || !metadata.stitchedImageSize || !metadata.images) {
        return { valid: false, message: '元数据格式无效，缺少必要字段' };
      }
      
      // Check if metadata version is supported
      if (metadata.version !== '1.0') {
        return { valid: false, message: `不支持的元数据版本: ${metadata.version}` };
      }
      
      // Check if metadata has at least one image
      if (metadata.images.length === 0) {
        return { valid: false, message: '元数据不包含任何图片信息' };
      }
      
      // Check if image dimensions match metadata
      const img = await this.createImageFromFile(image);
      
      // Use the utility function to validate dimensions
      const dimensionsValidation = validateMetadataDimensions(
        metadata, 
        img.width, 
        img.height
      );
      
      if (!dimensionsValidation.valid) {
        return dimensionsValidation;
      }
      
      // Release the object URL to prevent memory leaks
      URL.revokeObjectURL(img.src);
      
      return { valid: true };
    } catch (error: any) {
      return { valid: false, message: `验证元数据时出错: ${error.message}` };
    }
  }
  
  /**
   * Create an image element from a file
   * @param file Image file
   * @returns Promise with HTMLImageElement
   */
  private async createImageFromFile(file: File): Promise<HTMLImageElement> {
    return new Promise((resolve, reject) => {
      const img = new Image();
      img.onload = () => resolve(img);
      img.onerror = () => reject(new Error('Failed to load image'));
      img.src = URL.createObjectURL(file);
    });
  }

  /**
   * Restore original images from stitched image
   * @param stitchedImage Stitched image file
   * @param metadata Image metadata
   * @param onProgress Optional callback for progress updates
   * @returns Promise with array of restored images
   */
  async restoreImages(
    stitchedImage: File, 
    metadata: ImageMetadata,
    onProgress?: (progress: number) => void,
    options?: {
      jpegQuality?: number;
      pngQuality?: number;
      preserveFormat?: boolean;
    }
  ): Promise<RestoredImage[]> {
    // Validate metadata against image
    const validation = await this.validateMetadata(stitchedImage, metadata);
    if (!validation.valid) {
      throw new ValidationError(validation.message || '元数据与图片不匹配');
    }
    
    return new Promise<RestoredImage[]>(async (resolve, reject) => {
      try {
        // 导入工具类
        const { createInlineWorker, restorationWorkerCode } = await import('../utils/workerUtils');
        
        // 创建内联Worker
        const worker = createInlineWorker(restorationWorkerCode);
        
        // Load stitched image
        this.createImageFromFile(stitchedImage)
          .then(img => {
            // Create canvas for stitched image
            const stitchedCanvas = document.createElement('canvas');
            stitchedCanvas.width = img.width;
            stitchedCanvas.height = img.height;
            
            // Get context
            const stitchedCtx = stitchedCanvas.getContext('2d');
            if (!stitchedCtx) {
              reject(new ProcessingError('无法创建画布上下文'));
              worker.terminate();
              return;
            }
            
            // Draw stitched image on canvas
            stitchedCtx.drawImage(img, 0, 0);
            
            // Release object URL to prevent memory leaks
            URL.revokeObjectURL(img.src);
            
            // Get image data
            const stitchedImageData = stitchedCtx.getImageData(
              0, 
              0, 
              stitchedCanvas.width, 
              stitchedCanvas.height
            );
            
            // Prepare data for worker
            // We need to convert ImageData to a transferable format
            // Also ensure metadata is cloneable by creating a clean copy
            const workerData = {
              stitchedImageData: {
                width: stitchedImageData.width,
                height: stitchedImageData.height,
                data: stitchedImageData.data.buffer
              },
              metadata: {
                version: metadata.version,
                timestamp: metadata.timestamp,
                stitchedImageSize: {
                  width: metadata.stitchedImageSize.width,
                  height: metadata.stitchedImageSize.height
                },
                backgroundColor: metadata.backgroundColor,
                images: metadata.images.map(img => ({
                  id: img.id,
                  name: img.name,
                  x: img.x,
                  y: img.y,
                  width: img.width,
                  height: img.height,
                  format: img.format,
                  rotated: img.rotated,
                  originalWidth: img.originalWidth,
                  originalHeight: img.originalHeight
                }))
              }
            };
            
            // Create a list of transferable objects
            const transferables = [stitchedImageData.data.buffer];
            
            // Handle messages from worker
            worker.onmessage = (e: MessageEvent) => {
              const { type, data } = e.data;
              
              if (type === 'progress' && onProgress) {
                onProgress(data);
              } else if (type === 'complete') {
                // Worker has finished extracting images
                // Now we need to create canvases and URLs for the restored images
                const restoredImages: RestoredImage[] = [];
                
                for (const restoredImageData of data.restoredImages) {
                  // Create canvas for restored image
                  const canvas = document.createElement('canvas');
                  canvas.width = restoredImageData.width;
                  canvas.height = restoredImageData.height;
                  
                  // Get context
                  const ctx = canvas.getContext('2d');
                  if (!ctx) {
                    continue;
                  }
                  
                  // Convert buffer back to ImageData
                  const imageData = new ImageData(
                    new Uint8ClampedArray(restoredImageData.imageDataBuffer),
                    restoredImageData.width,
                    restoredImageData.height
                  );
                  
                  // Draw image data on canvas
                  ctx.putImageData(imageData, 0, 0);
                  
                  // Create restored image object
                  // 使用用户设置的格式和质量
                  const useOriginalFormat = options?.preserveFormat !== false;
                  const format = useOriginalFormat ? restoredImageData.format : 'jpeg';
                  const mimeType = format === 'png' ? 'image/png' : 'image/jpeg';
                  const quality = format === 'png' 
                    ? (options?.pngQuality ?? 1.0) 
                    : (options?.jpegQuality ?? 0.95);
                  
                  const restoredImage: RestoredImage = {
                    id: restoredImageData.id,
                    name: restoredImageData.name,
                    canvas,
                    width: restoredImageData.width,
                    height: restoredImageData.height,
                    url: canvas.toDataURL(mimeType, quality),
                    format: restoredImageData.format
                  };
                  
                  // Add to restored images array
                  restoredImages.push(restoredImage);
                }
                
                resolve(restoredImages);
                worker.terminate();
              } else if (type === 'error') {
                reject(new ProcessingError(data.message));
                worker.terminate();
              }
            };
            
            // Handle worker errors
            worker.onerror = (error) => {
              reject(new ProcessingError('Worker error: ' + error.message));
              worker.terminate();
            };
            
            // Start the worker with transferable objects
            // @ts-ignore - TypeScript doesn't recognize the transferables parameter correctly
            worker.postMessage(workerData, transferables);
          })
          .catch(error => {
            reject(error);
            worker.terminate();
          });
      } catch (error) {
        reject(error);
      }
    });
  }

  /**
   * Download a single restored image
   * @param image Restored image to download
   */
  async downloadRestoredImage(
    image: RestoredImage, 
    options?: {
      jpegQuality?: number;
      pngQuality?: number;
      preserveFormat?: boolean;
    }
  ): Promise<void> {
    try {
      // 使用用户设置的格式和质量
      const useOriginalFormat = options?.preserveFormat !== false;
      const format = useOriginalFormat ? image.format : 'jpeg';
      const mimeType = format === 'png' ? 'image/png' : 'image/jpeg';
      const quality = format === 'png' 
        ? (options?.pngQuality ?? 1.0) 
        : (options?.jpegQuality ?? 0.95);
      
      // Convert canvas to blob
      const blob = await new Promise<Blob>((resolve, reject) => {
        image.canvas.toBlob(
          (blob) => {
            if (blob) {
              resolve(blob);
            } else {
              reject(new Error('Failed to convert canvas to blob'));
            }
          },
          mimeType,
          quality
        );
      });
      
      // Create a URL for the blob
      const url = URL.createObjectURL(blob);
      
      // Create a link element
      const link = document.createElement('a');
      link.href = url;
      link.download = image.name;
      
      // Append the link to the document
      document.body.appendChild(link);
      
      // Click the link to trigger the download
      link.click();
      
      // Remove the link from the document
      document.body.removeChild(link);
      
      // Release the URL
      URL.revokeObjectURL(url);
    } catch (error) {
      console.error('下载图片失败:', error);
      throw error;
    }
  }

  /**
   * Download restored images as zip archive
   * @param images Restored images to download
   * @param onProgress Optional callback for progress updates
   */
  async downloadAsZip(
    images: RestoredImage[],
    onProgress?: (progress: number) => void,
    options?: {
      jpegQuality?: number;
      pngQuality?: number;
      preserveFormat?: boolean;
    }
  ): Promise<void> {
    try {
      // Import JSZip
      const JSZip = (await import('jszip')).default;
      const FileSaver = (await import('file-saver')).default;
      
      // Create a new zip file
      const zip = new JSZip();
      
      // Add each image to the zip file
      const totalImages = images.length;
      
      for (let i = 0; i < totalImages; i++) {
        const image = images[i];
        
        // Convert canvas to blob
        const blob = await new Promise<Blob>((resolve, reject) => {
          // 使用用户设置的格式和质量
          const useOriginalFormat = options?.preserveFormat !== false;
          const format = useOriginalFormat ? image.format : 'jpeg';
          const mimeType = format === 'png' ? 'image/png' : 'image/jpeg';
          const quality = format === 'png' 
            ? (options?.pngQuality ?? 1.0) 
            : (options?.jpegQuality ?? 0.95);
            
          image.canvas.toBlob(
            (blob) => {
              if (blob) {
                resolve(blob);
              } else {
                reject(new Error('Failed to convert canvas to blob'));
              }
            },
            mimeType,
            quality
          );
        });
        
        // Add blob to zip file
        zip.file(image.name, blob);
        
        // Update progress
        if (onProgress) {
          onProgress(Math.round(((i + 1) / totalImages) * 50)); // First 50% for adding files
        }
      }
      
      // Generate zip file
      const zipBlob = await zip.generateAsync({ type: 'blob' }, (metadata) => {
        if (onProgress) {
          // Map progress from 50% to 100%
          onProgress(50 + Math.round(metadata.percent / 2));
        }
      });
      
      // Download zip file
      FileSaver.saveAs(zipBlob, 'restored-images.zip');
      
      // Complete progress
      if (onProgress) {
        onProgress(100);
      }
    } catch (error) {
      console.error('下载ZIP文件失败:', error);
      throw error;
    }
  }
}

export default new ImageRestorationService();